package cevent.source.cloudcenter.source.controller.admin;/**
 * Created by Cevent on 2021/4/24.
 */

import cevent.source.cloudcenter.server.dto.FileDto;
import cevent.source.cloudcenter.server.dto.ResponseDataDto;
import cevent.source.cloudcenter.server.enums.UseTypeEnum;
import cevent.source.cloudcenter.server.service.FileService;
import cevent.source.cloudcenter.server.util.Base64ToMultipartFile;
import cevent.source.cloudcenter.server.util.UUID8Util;
import cevent.source.cloudcenter.server.util.VODUtil;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.vod.model.v20170321.GetMezzanineInfoResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author cevent
 * @description 文件上传：单图
 * @date 2021/4/24 14:41
 */
@RestController
@RequestMapping("/admin")
public class UploadIconController {
    private static final Logger LOG = LoggerFactory.getLogger(UploadIconController.class);
    public static final String BUSINESS_NAME = "文件上传模块";

    @Resource
    private FileService fileService;

    //注入properties中的file配置
    @Value("${file.targetPath}")
    private String FILE_TARGET_PATH;
    @Value("${file.getPath}")
    private String FILE_GET_PATH;
    @Value("${vod.accessKeyID}")
    private String VOD_ACCESS_KEY_ID;
    @Value("${vod.accessKeySecret}")
    private String VOD_ACCESS_KEY_SECRET;
    /*1.单图上传：
    @RequestParam：基于文件是formData形式传输
    MultipartFile：富文本大文件
    file：formData.append('file',icon)，前端formData中提交file参数名，包含元素查询中的icon文件
     */
    @RequestMapping("/test")
    public String test() {
        return "文件系统模块启动成功";
    }

    @RequestMapping("/upload/icon")
    public ResponseDataDto uploadIcon(@RequestParam MultipartFile file, String moduleId, String useType) throws IOException {
        LOG.info("上传的文件file：{}", file);
        LOG.info("上传的文件file名fileName：{}", file.getOriginalFilename());
        LOG.info("上传的文件file大小：{}", file.getSize());
        LOG.info("上传的文件file名name：{}", file.getName());
        LOG.info("上传的文件file类型：{}", file.getContentType());

        //保存文件到本地
        String fileName = file.getOriginalFilename();
        String fileKey = UUID8Util.getShortUUID();
        //获取文件后缀名(类型)，全部转小写
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        String contentType = file.getContentType();
        Long size = file.getSize();

        //文件名配置
        Date now = new Date();
        String date = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(now);

        //获取枚举类型，生成新的文件夹路径
        UseTypeEnum useTypeEnum = UseTypeEnum.getByCode(useType);
        //获取到枚举名SOURCE("","")
        String dateDir = new SimpleDateFormat("yyyy_MM_dd").format(now);
        String dir = useTypeEnum.name().toLowerCase();
        File fullDir = new File(FILE_TARGET_PATH + "source" + File.separator + dir + File.separator + dateDir);
        LOG.info("创建的fullDir目录：{}", fullDir);
        //创建文件夹
        if (!fullDir.exists()) {
            fullDir.mkdirs();
            LOG.info("创建成功目录？：{}", fullDir.mkdirs());
        }
        String name = dateDir + "_" + fileKey + "_" + fileName;
        //File.separator生成不同操作系统的/斜杠
        String path = "source/" + dir + File.separator + dateDir + File.separator + name;
        LOG.info("上传的path：{}", path);
        //全路径
        String fullPath = FILE_TARGET_PATH + path;
        //放入日期目录
        File dest = new File(fullPath);
        file.transferTo(dest);
        LOG.info("获取destination目的位置完全路径：{}", dest.getAbsolutePath());

        //保存file
        FileDto fileDto = new FileDto();
        fileDto.setName(name);
        //返回相对路径
        //fileDto.setPath(path);
        //返回全路径
        fileDto.setPath(FILE_GET_PATH + path);
        fileDto.setSuffix(suffix);
        fileDto.setContentType(contentType);
        fileDto.setSize(Math.toIntExact(size));
        fileDto.setUseType(useType);
        fileDto.setIsShow(0);
        fileDto.setModuleId(moduleId);

        fileService.save(fileDto);
        ResponseDataDto responseData = new ResponseDataDto();
        responseData.setResponseData(fileDto);
        return responseData;
    }

    //合并分片请求
    @GetMapping("/file/shard/merge")
    public ResponseDataDto shardMerge() throws IOException {
        //指定合并的文件
        File mergeFile = new File(FILE_TARGET_PATH + "/source/source_public/2021_04_28/mergeVideo.mp4");
        //开启文件追加写入
        FileOutputStream outputStream = new FileOutputStream(mergeFile, true);
        //输入的分片文件
        FileInputStream inputStream = null;
        byte[] bytes = new byte[10 * 1024 * 1024];
        int length;

        //读写分片
        try {
            //第一个分片
            inputStream = new FileInputStream(new File(FILE_TARGET_PATH + "source/source_public/2021_04_28/2021-04-28 10-02-49_fk2cdp8P_blob"));
            while ((length = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, length);
            }
            //第二个分片
            inputStream = new FileInputStream(new File(FILE_TARGET_PATH + "source/source_public/2021_04_28/2021-04-28 10-06-51_FrFGayay_blob"));
            while ((length = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, length);
            }
            //第三个分片
            inputStream = new FileInputStream(new File(FILE_TARGET_PATH + "source/source_public/2021_04_28/2021-04-28 10-07-29_Ua0xPQXd_blob"));
            while ((length = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, length);
            }
        } catch (IOException e) {
            LOG.error("合并分片错误", e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                    LOG.info("fileInputStream关闭");
                }
                outputStream.close();
                LOG.info("fileOutputStream关闭");
            } catch (IOException e) {
                LOG.info("IO流关闭catch：", e);
            }
        }
        ResponseDataDto responseData = new ResponseDataDto();
        responseData.setResponseMsg("文件分片shardFile合并merge成功！");
        return responseData;
    }

    /**
     * 基于前端封装了param参数，并将shardFile转为base64，这里的@RequestParam
     *
     * @return
     * @throws IOException
     * @RequestParam MultipartFile shard,
     * Integer size,
     * Integer shardIndex,
     * Integer shardSize,
     * Integer shardTotal,
     * String shardKey,
     * String name,
     * String suffix,
     * String moduleId,
     * String useType
     * 直接换成@RequestBody FileDto接收
     */
    @RequestMapping("/upload/bigfile")
    public ResponseDataDto uploadBigFile(@RequestBody FileDto fileDto) throws IOException, InterruptedException {

        //保存文件到本地
        //String fileKey = UUID8Util.getShortUUID();
        String useType = fileDto.getUseType();
        LOG.info("传入的useType：{}", useType);
        //接收前端传入的shard=string
        String shardBase64 = fileDto.getShard();
        //将base64字符串转为File
        MultipartFile shard = Base64ToMultipartFile.base64TransToMultipartFile(shardBase64);
        //文件名配置
        Date now = new Date();
        String date = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(now);

        //获取枚举类型，生成新的文件夹路径
        UseTypeEnum useTypeEnum = UseTypeEnum.getByCode(useType);
        //获取到枚举名SOURCE("","")
        String dateDir = new SimpleDateFormat("yyyy_MM_dd").format(now);
        String dir = useTypeEnum.name().toLowerCase();
        File fullDir = new File(FILE_TARGET_PATH + "source" + File.separator + dir + File.separator + dateDir);
        LOG.info("分片传输--》创建的fullDir目录：{}", fullDir);
        //创建文件夹
        if (!fullDir.exists()) {
            fullDir.mkdirs();
            LOG.info("分片传输--》创建成功目录？：{}", fullDir.mkdirs());
        }
        String mergeName = dateDir + "_" + fileDto.getShardKey() + "_" + fileDto.getName();
        /**
         * File.separator生成不同操作系统的/斜杠
         * 更新分片传入，将每个分片文件后缀改为xx.mp4.shardIndex
         */
        //String path="source/"+dir+File.separator+dateDir+File.separator+fileDto.getName()+"."+fileDto.getShardIndex();
        //string调用参数过多，采用stringBuffer，最后转toString()
        //保存到数据库的path
        String path = new StringBuffer("source/")
                .append(dir)
                .append(File.separator).append(dateDir)
                .append(File.separator).append(dateDir).append("_")
                .append(fileDto.getShardKey()).append("_")
                .append(fileDto.getName())
                .toString();//.append(fileKey)
        //本地存放的path+.index索引
        String pathLocal = new StringBuffer(path)
                .append(".").append(fileDto.getShardIndex()).toString();//.append(fileKey)
        LOG.info("分片传输--》上传的path：{}", path);
        //全路径
        String fullPath = FILE_TARGET_PATH + pathLocal;
        //放入日期目录
        File dest = new File(fullPath);
        shard.transferTo(dest);
        LOG.info("分片传输--》获取destination目的位置完全路径：{}", dest.getAbsolutePath());

        //保存file,返回相对路径
        //fileDto.setPath(path);
        //返回全路径
        fileDto.setPath(FILE_GET_PATH + path);
        //改用requestBody传参，这里不需要set已经存在在dto中的属性
        fileDto.setName(mergeName);
        fileDto.setIsShow(0);
        //基本数据类型使用：==  对象使用：.equals()
        if (fileDto.getShardIndex() .equals(fileDto.getShardTotal()) ) {
            this.mergeFile(fileDto);
        }
        fileService.save(fileDto);
        ResponseDataDto responseData = new ResponseDataDto();
        responseData.setResponseData(fileDto);
        return responseData;
    }

    //合并分片
    private String mergeFile(FileDto fileDto) throws IOException, InterruptedException {
        String path = fileDto.getPath();
        //将传入的全路径path，截取前段
        path = path.replace(FILE_GET_PATH, "");
        Integer shardTotal = fileDto.getShardTotal();
        //指定合并的文件
        File mergeFile = new File(FILE_TARGET_PATH + path);
        //开启文件追加写入
        FileOutputStream outputStream = new FileOutputStream(mergeFile, true);
        //输入的分片文件
        FileInputStream inputStream = null;
        byte[] bytes = new byte[10 * 1024 * 1024];
        int length;

        //读写分片，根据shardTotal循环合并分片
        try {
            for (int i = 0; i < shardTotal; i++) {
                //分片合并
                inputStream = new FileInputStream(new File(FILE_TARGET_PATH + path + "." + (i + 1)));
                while ((length = inputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, length);
                }
            }
        } catch (IOException e) {
            LOG.error("合并分片错误", e);
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                    LOG.info("fileInputStream关闭");
                }
                outputStream.close();
                LOG.info("fileOutputStream关闭");
            } catch (IOException e) {
                LOG.info("IO流关闭catch：", e);
            }
        }
        /*
        启用垃圾回收，清空之前的IO流数据，否则，这里的File将无法完成删除操作（FILE被程序占用）
        java的gc垃圾回收，是先将已有程序执行完毕再回收，有可能出现分片删除失败，需要加入线程
         */
        System.gc();
        //加入线程阻塞，避免出现分片删除失败
        Thread.sleep(100);
        //删除磁盘分片
        for(int i=0;i<shardTotal;i++){
            String filePath=FILE_TARGET_PATH+path+"."+(i+1);
            File file=new File(filePath);
            boolean result=file.delete();
            LOG.info("删除{}，{}",filePath,result?"成功":"失败");
        }
        String mergeResult = "文件分片shardFile合并merge成功！";
        return mergeResult;
    }
    //文件上传：分片检查
    @GetMapping("/check/file/{shardKey}")
    public ResponseDataDto checkShardKey(@PathVariable String shardKey) throws ClientException {
        ResponseDataDto responseData=new ResponseDataDto();
        FileDto fileDto=fileService.getByShardKey(shardKey);
        //文件存在判断，存在则加入path
        if(fileDto!=null){
            if(StringUtils.isEmpty(fileDto.getVod())){
                fileDto.setPath(fileDto.getPath());
                LOG.info("/check/file/获取到的files：{}",fileDto);
            }else{
                DefaultAcsClient acsClient= VODUtil.initVODClient(VOD_ACCESS_KEY_ID,VOD_ACCESS_KEY_SECRET);
                GetMezzanineInfoResponse mezzanineInfoResponse=VODUtil.getMezzanineInfoResponse(acsClient,fileDto.getVod());
                LOG.info("响应的mezzanine中间层VOD文件数据：{}", JSON.toJSONString(mezzanineInfoResponse));
                String fileURL=mezzanineInfoResponse.getMezzanine().getFileURL();
                fileDto.setPath(fileURL);
            }

        }
        responseData.setResponseData(fileDto);
        return responseData;
    }
}
